﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MyFeer.Widget.Grid.Cell;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows;
using MyFeer.Widget.Grid;
using System.Windows.Media;
using MyFeer.Lib.common;

namespace MyFeer.Widget.Grid
{
    public class GridViewer:ContentControl
    {
        private GridModel gridModel = new GridModel(20,20);
        private ColumnHeader columnHeader;
        private RowHeader rowHeader;
        private Canvas mainCanvas;
        private Canvas gridCanvas;
        private List<int> visibleRowIndexs;
        private List<int> visibleColumnIndexs;
        
        private IntegerPoint fixedPos = new IntegerPoint(2, 2);
        private int scrollOffsetCol =1 ;
        private int scrollOffsetRow = 1;
        private IntegerRect selectRect;
        private Dictionary<IntegerPoint, CellItem> cellItemMap = new Dictionary<IntegerPoint, CellItem>();
        private CellEditEventListener cellEditEventListener;
        private CellSelectEventListener cellSelectEventListener;
        public GridViewer()
        {
            gridModel.Unites.Add(new IntegerRect(5, 5, 7, 8));
            this.DefaultStyleKey = typeof(GridViewer);
            this.SizeChanged += new SizeChangedEventHandler(GridViewer_SizeChanged);
            rowHeader = new RowHeader(this);
            columnHeader=new ColumnHeader(this);
            cellEditEventListener = new CellEditEventListener(this);
            cellSelectEventListener = new CellSelectEventListener(this);

            this.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(cellEditEventListener.GridViewer_MouseLeftButtonUp);
        }

        internal List<int> VisibleRowIndexs
        {
            get { return visibleRowIndexs; }
        }

        internal List<int> VisibleColumnIndexs
        {
            get { return visibleColumnIndexs; }
        }

        internal CellItem GetCellItem(int rowIndex, int colIndex)
        {
            IntegerPoint pos = new IntegerPoint(rowIndex, colIndex);
            return this.cellItemMap[pos];

        }

        internal Canvas GridCanvas
        {
            get{return this.gridCanvas;}
            
        }

        public GridModel GridModel
        {
            get
            {
                return this.gridModel;
            }
        }
        public IntegerRect SelectRect
        {
            get
            {
                return selectRect;
            }
            set
            {
                this.selectRect = value;
            }
        }

        public Canvas MainCanvas
        {
            get { return this.mainCanvas; }
        }


        void GridViewer_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            this.Refersh();

        }
        public void Refersh()
        {
            try
            {
                base.UpdateLayout();
                if (this.gridCanvas != null)
                {
                    this.gridCanvas.Children.Clear();
                }
                
                this.mainCanvas.Children.Clear();
                Size canvasSize = this.RenderSize;
                Size gridSize = this.getGridSize();
                this.RenderCells();
                if (gridSize.Width > canvasSize.Width)
                {
                    ScrollBar hScrollBar = new ScrollBar();
                    hScrollBar.Maximum = this.gridModel.getColumnPos().Count-this.visibleColumnIndexs.Count+1;
                    hScrollBar.Minimum = 0;
                    hScrollBar.Width = canvasSize.Width - GridConst.DefaultScrollWidth;
                    hScrollBar.Height = GridConst.DefaultScrollWidth;
                    hScrollBar.Orientation = Orientation.Horizontal;
                    hScrollBar.ValueChanged += new RoutedPropertyChangedEventHandler<double>(hScrollBar_ValueChanged);
                    hScrollBar.Value = this.scrollOffsetCol;
                    this.mainCanvas.Children.Add(hScrollBar);
                    Canvas.SetTop(hScrollBar, canvasSize.Height - GridConst.DefaultScrollWidth - 2);
                    Canvas.SetLeft(hScrollBar, 0);
                }

                if (gridSize.Height > canvasSize.Height)
                {
                    ScrollBar vScrollBar = new ScrollBar();
                    vScrollBar.Maximum = this.gridModel.getRowPs().Count-this.visibleRowIndexs.Count+1;
                    vScrollBar.Minimum = 0;
                    vScrollBar.Height = canvasSize.Height - GridConst.DefaultScrollWidth;
                    vScrollBar.Width = GridConst.DefaultScrollWidth;
                    vScrollBar.Orientation = Orientation.Vertical;
                    vScrollBar.ValueChanged += new RoutedPropertyChangedEventHandler<double>(vScrollBar_ValueChanged);
                    vScrollBar.Value = this.scrollOffsetRow;
                    this.mainCanvas.Children.Add(vScrollBar);
                    Canvas.SetTop(vScrollBar, 0);
                    Canvas.SetLeft(vScrollBar, canvasSize.Width - GridConst.DefaultScrollWidth - 2);
                }
                RectangleGeometry geometry = new RectangleGeometry();
                geometry.Rect = new Rect(0, 0, canvasSize.Width - 2, canvasSize.Height - 2);
                this.mainCanvas.Clip = geometry;
            }
            catch (Exception ex)
            {
                Console.Write(ex.StackTrace);
            }
           
        }

        

        /// <summary>
        /// 根据坐标获取表格模型
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public CellModel getCellModel(int x, int y)
        {
            Size canvasSize=this.mainCanvas.RenderSize;
            if (x < GridConst.DefaultRowHeaderWidth || x > canvasSize.Width)
                return null;
            if (y < GridConst.DefaultColumnHeaderHeight || y > mainCanvas.Height)
                return null;
            x = x - GridConst.DefaultRowHeaderWidth;
            y = y - GridConst.DefaultColumnHeaderHeight;
            int colIndex=-1;
            int colPos = 0;
            int rowIndex = -1;
            int rowPos = 0;
            for (int i = 0; i < this.visibleColumnIndexs.Count; i++)
            {
                int visibleColumnIndex = this.visibleColumnIndexs[i];
                int colWidth=this.gridModel.getColumnWidth(visibleColumnIndex);
                colPos += colWidth;
                if (colPos > x)
                {
                    colIndex = visibleColumnIndex;
                    break;
                }
            }
            for (int i = 0; i < this.visibleRowIndexs.Count; i++)
            {
                int visibleRowIndex = this.visibleRowIndexs[i];
                int rowHeight = this.gridModel.getRowHeight(visibleRowIndex);
                rowPos += rowHeight;
                if (rowPos > y)
                {
                    rowIndex = visibleRowIndex;
                    break;
                }
            }
            foreach (IntegerRect rect in this.gridModel.Unites)
            {
                if (rect.Contain(colIndex,rowIndex))
                {
                    return this.gridModel.getCellModel(rect.Top,rect.Left);
                }
            }
            return this.gridModel.getCellModel(rowIndex,colIndex);
        }

        public IntegerRect getCellArea(int rowIndex, int colIndex)
        {
            int colPos = GridConst.DefaultRowHeaderWidth;
            int rowPos = GridConst.DefaultColumnHeaderHeight;
            int colWidth = 0;
            int rowHeight = 0;
            for (int i = 0; i < this.visibleColumnIndexs.Count; i++)
            {
                int visibleColumnIndex = this.visibleColumnIndexs[i];
                colWidth = this.gridModel.getColumnWidth(visibleColumnIndex);
                if (visibleColumnIndex ==colIndex)
                {
                    break;
                }
                colPos += colWidth;
            }
            for (int i = 0; i < this.visibleRowIndexs.Count; i++)
            {
                int visibleRowIndex = this.visibleRowIndexs[i];
                rowHeight = this.gridModel.getRowHeight(visibleRowIndex);
                if (rowIndex==visibleRowIndex)
                {
                    rowIndex = visibleRowIndex;
                    break;
                }
                rowPos += rowHeight;
            }
            int lastColIndex = this.visibleColumnIndexs[this.visibleColumnIndexs.Count - 1];
            int lastRowIndex = this.visibleRowIndexs[this.visibleRowIndexs.Count - 1];
            foreach (IntegerRect rect in this.gridModel.Unites)
            {
                IntegerPoint startCellPos = new IntegerPoint(0, 0);
                IntegerPoint endCellPos = new IntegerPoint(0, 0);
                if (!(rect.Left == colIndex && rect.Top == rowIndex))
                {
                    continue;
                }

                Boolean isContain = false;
                if (rect.Left < this.fixedPos.X && rect.Top < this.fixedPos.Y)
                {
                    endCellPos.X = this.fixedPos.X;
                    endCellPos.Y = this.fixedPos.Y;
                    isContain = true;
                }
                else if ((rect.Left < lastColIndex && rect.Right > (this.fixedPos.X + this.scrollOffsetCol)) &&
                    (rect.Top < lastRowIndex && rect.Bottom > (this.fixedPos.Y + this.scrollOffsetRow)))
                {
                    if (rect.Left < this.fixedPos.X + this.scrollOffsetCol)
                    {
                        startCellPos.X = this.fixedPos.X + this.scrollOffsetCol;
                    }
                    else
                    {
                        startCellPos.X = rect.Left;
                    }
                    if (rect.Top < this.fixedPos.Y + this.scrollOffsetRow)
                    {
                        startCellPos.Y = this.fixedPos.Y + this.scrollOffsetRow;
                    }
                    else
                    {
                        startCellPos.Y = rect.Top;
                    }
                    if (rect.Right > lastColIndex)
                    {
                        endCellPos.X = lastColIndex;
                    }
                    else
                    {
                        endCellPos.X = rect.Right;
                    }
                    if (rect.Bottom > lastRowIndex)
                    {
                        endCellPos.Y = lastRowIndex;
                    }
                    else
                    {
                        endCellPos.Y = rect.Bottom;
                    }
                    isContain = true;

                }
                if (isContain)
                {
                    colPos = GridConst.DefaultRowHeaderWidth;
                    rowPos = GridConst.DefaultColumnHeaderHeight;
                    colWidth = 0;
                    rowHeight = 0;
                    for (int i = 0; i < this.visibleColumnIndexs.Count; i++)
                    {
                        int visibleColIndex=this.visibleColumnIndexs[i];
                        if ( visibleColIndex== startCellPos.X)
                        {
                            break;
                        }
                        colPos += this.gridModel.getColumnWidth(visibleColIndex);
                    }
                    for (int i = 0; i < this.visibleRowIndexs.Count; i++)
                    {
                        int visibleRowIndex = this.visibleRowIndexs[i];
                        if (visibleRowIndex == startCellPos.Y)
                        {
                            break;
                        }
                        rowPos += this.gridModel.getRowHeight(visibleRowIndex);
                    }
                    for (int i = startCellPos.X; i <= endCellPos.X; i++)
                    {
                        colWidth += this.gridModel.getColumnWidth(i);
                    }

                    for (int i = startCellPos.Y; i <= endCellPos.Y; i++)
                    {
                        rowHeight += this.gridModel.getRowHeight(i);
                    }
                }
            }
            return new IntegerRect(colPos, rowPos, colPos+colWidth, rowPos+rowHeight);

        }


        private void RenderCells()
        {
            this.cellItemMap.Clear();
            Size canvasSize = this.RenderSize;

            gridCanvas = new Canvas();
            gridCanvas.Width = canvasSize.Width;
            gridCanvas.Height = canvasSize.Height;
            Canvas.SetLeft(this.mainCanvas, 0);
            Canvas.SetTop(this.mainCanvas, 0);
            this.mainCanvas.Children.Add(gridCanvas);

            visibleColumnIndexs=this.getColVisibleIndexs();
            visibleRowIndexs = this.getRowVisibleIndexs();
            Point startPos=new Point(0,0);
            Boolean drawColumnHeader = true;
            startPos.Y = GridConst.DefaultColumnHeaderHeight;
            for (int i = 0; i < visibleRowIndexs.Count; i++)
            {
                startPos.X = GridConst.DefaultRowHeaderWidth;
                int rowIndex = visibleRowIndexs[i];
                int cellHeight = this.gridModel.getRowHeight(rowIndex);
                this.rowHeader.render(rowIndex, (int)startPos.Y, cellHeight);
                for (int j = 0; j < visibleColumnIndexs.Count; j++)
                {
                    int colIndex = visibleColumnIndexs[j];
                    int cellWidth = this.gridModel.getColumnWidth(colIndex);
                    if (drawColumnHeader)
                    {
                        this.columnHeader.render(colIndex, (int)startPos.X, cellWidth);
                    }

                    CellModel cellModel = this.gridModel.getCellModel(rowIndex, colIndex);

                    CellItem cell = new CellItem(this, cellModel);
                    cell.render((int)startPos.X, (int)startPos.Y, cellWidth, cellHeight);
                    this.cellItemMap.Add(new IntegerPoint(rowIndex,colIndex), cell);

                    startPos.X += cellWidth;
                    if (startPos.X > canvasSize.Width)
                        break;
                }
                drawColumnHeader = false;
                startPos.Y += cellHeight;
                if (startPos.Y > canvasSize.Height)
                    break;
            }

            List<IntegerRect> unites=this.gridModel.Unites;
            int lastColIndex=this.visibleColumnIndexs[this.visibleColumnIndexs.Count - 1];
            int lastRowIndex = this.visibleRowIndexs[this.visibleRowIndexs.Count - 1];
            foreach(IntegerRect rect in unites)
            {
                IntegerPoint startCellPos = new IntegerPoint(0, 0);
                IntegerPoint endCellPos = new IntegerPoint(0, 0);
                Boolean isContain=false;
                if (rect.Left < this.fixedPos.X && rect.Top < this.fixedPos.Y)
                {
                    endCellPos.X = this.fixedPos.X;
                    endCellPos.Y = this.fixedPos.Y;
                    isContain = true;
                }
                else if((rect.Left<lastColIndex && rect.Right>(this.fixedPos.X+this.scrollOffsetCol))&&
                    (rect.Top<lastRowIndex && rect.Bottom>(this.fixedPos.Y+this.scrollOffsetRow)))
                {
                    if (rect.Left < this.fixedPos.X + this.scrollOffsetCol)
                    {
                        startCellPos.X = this.fixedPos.X + this.scrollOffsetCol;
                    }
                    else
                    {
                        startCellPos.X = rect.Left;
                    }
                    if (rect.Top < this.fixedPos.Y + this.scrollOffsetRow)
                    {
                        startCellPos.Y = this.fixedPos.Y + this.scrollOffsetRow;
                    }
                    else
                    {
                        startCellPos.Y = rect.Top;
                    }
                    if (rect.Right > lastColIndex)
                    {
                        endCellPos.X = lastColIndex;
                    }
                    else
                    {
                        endCellPos.X = rect.Right;
                    }
                    if (rect.Bottom > lastRowIndex)
                    {
                        endCellPos.Y = lastRowIndex;
                    }
                    else
                    {
                        endCellPos.Y = rect.Bottom;
                    }
                    isContain = true;
                   
                }
                if (isContain)
                {
                    IntegerRect cellArea=this.getCellArea(startCellPos.Y, startCellPos.X);
                    int cellWidth = 0;
                    int cellHeight = 0;
                    for (int i = startCellPos.X; i <= endCellPos.X; i++)
                    {
                        cellWidth += this.gridModel.getColumnWidth(i);
                    }
                    for (int i = startCellPos.Y; i <= endCellPos.Y; i++)
                    {
                        cellHeight += this.gridModel.getRowHeight(i);
                    }
                    CellModel cellModel = this.gridModel.getCellModel(rect.Top, rect.Left);
                    CellItem cell = new CellItem(this, cellModel);
                    cell.render(cellArea.Left, cellArea.Top, cellWidth, cellHeight);
                    IntegerPoint cellKey=new IntegerPoint(rect.Top, rect.Left);
                    this.cellItemMap.Remove(cellKey);
                    this.cellItemMap.Add(cellKey, cell);
                }
            }

            if (this.selectRect != null)
            {
                IntegerRect cellRect = this.getCellArea(selectRect.Top, selectRect.Left);
                Border selectCellStyle = GridConst.SelectedCellStyle;
                this.gridCanvas.Children.Remove(selectCellStyle);
                Canvas.SetLeft(selectCellStyle, cellRect.Left);
                Canvas.SetTop(selectCellStyle, cellRect.Top);
                selectCellStyle.Width = cellRect.Width;
                selectCellStyle.Height = cellRect.Height;
                this.gridCanvas.Children.Add(selectCellStyle);
            }
            
        }

        /// <summary>
        /// 获取行可见的索引
        /// </summary>
        private List<int> getRowVisibleIndexs()
        {
            List<int> rowIndexs = new List<int>();
            int fixedHeight = GridConst.DefaultColumnHeaderHeight;
            if (this.fixedPos.Y > 0)
            {
                fixedHeight+=this.gridModel.getRowPs()[(int)this.fixedPos.Y-1];
                for (int i = 0; i < this.fixedPos.Y; i++)
                {
                    rowIndexs.Add(i);
                }
            }
            List<int> rows=this.gridModel.getRowPs();
            Size canvasSize=this.RenderSize;
            int noFixedHeight = (int)canvasSize.Height - fixedHeight;
            int startIndex=(int)this.fixedPos.Y + this.scrollOffsetRow;
            int startPos = rows[startIndex];
            for (int i = startIndex; i < rows.Count; i++)
            {
                int nextPos=rows[i];
                rowIndexs.Add(i);
                if (nextPos - startPos >= noFixedHeight)
                {
                    break;
                }
            }
            return rowIndexs;
        }
        private List<int> getColVisibleIndexs()
        {
            List<int> colIndexs = new List<int>();
            int fixedWidth = GridConst.DefaultRowHeaderWidth;
            if (this.fixedPos.X > 0)
            {
                fixedWidth += this.gridModel.getColumnPos()[(int)this.fixedPos.X-1];
                for (int i = 1; i < this.fixedPos.Y; i++)
                {
                    colIndexs.Add(i-1);
                }
            }
            List<int> columns = this.gridModel.getColumnPos();
            Size canvasSize = this.RenderSize;
            int noFixedWidth = (int)canvasSize.Width - fixedWidth;
            int startIndex = (int)this.fixedPos.X + this.scrollOffsetCol;
            int startPos = columns[startIndex];
            for (int i = startIndex; i < columns.Count; i++)
            {
                int nextPos = columns[i];
                colIndexs.Add(i);
                if (nextPos - startPos >= noFixedWidth)
                {
                    break;
                }
            }
            return colIndexs;
        }


        

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            this.mainCanvas = (Canvas)this.GetTemplateChild("MainCanvas");
            //this.mainCanvas.SizeChanged += new SizeChangedEventHandler(mainCanvas_SizeChanged);
        }

        void mainCanvas_SizeChanged(object sender, SizeChangedEventArgs e)
        {
        }

        void hScrollBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            int hScrollOffset=(int)e.NewValue;
            if (hScrollOffset != this.scrollOffsetCol)
            {
                this.scrollOffsetCol = hScrollOffset;
                this.Refersh();
            }
        }
        void vScrollBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            int vScrollOffset = (int)e.NewValue;
            if (vScrollOffset != this.scrollOffsetRow)
            {
                this.scrollOffsetRow = vScrollOffset;
                this.Refersh();
            }
        }

        /// <summary>
        /// 获取区域的大小
        /// </summary>
        /// <returns></returns>
        private Size getGridSize()
        {
            List<int> columns=this.gridModel.getColumnPos();
            List<int> rows = this.gridModel.getRowPs();
            return new Size(columns[columns.Count - 1], rows[rows.Count-1]);
        }
        
    }
}
